package main;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import svg.Container;
import svg.JobCanvas;
import svg.JobViewer;
import svg.LinkCanvas;
import svg.ProcessorCanvas;
import utils.Edge;
import utils.GraphReader;
import utils.Matrix;
import utils.Processor;
import utils.Task;
import utils.TaskComparator;

public class Main 
{
	public static void main(String[] args) throws IOException, CloneNotSupportedException 
	{
		String input = null;
		int nbOfProcessors = 3;
		boolean duplicateTasks = true;
		try
		{
			input = args[0];
			if(args.length > 1)
			{
				nbOfProcessors = Integer.parseInt(args[1]);
			}
			if(args.length > 2)
			{
				duplicateTasks = Boolean.parseBoolean(args[2]);
			}
		}
		catch(Exception e)
		{
			System.err.println("<input file> <number of processors (default: " + 
					nbOfProcessors + ")> <duplicate tasks (default: " + 
					duplicateTasks + ")>");
			System.exit(0);
		}
		
		String output = input;
		if(duplicateTasks)
		{
			output += "-dup";
		}
		else
		{
			output += "-nodup";
		}
		
		// reads task graph and assigns priorities
		Integer[][] graph = GraphReader.getGraph(input);
		int[] length = Matrix.asapPriorities(graph);
		
		ArrayList<Task> tasks = new ArrayList<Task>();
		//Create tasks
		for(int i = 0; i < length.length; i++)
		{
			ArrayList<Task> parents = new ArrayList<Task>();
			
			//Get parent nodes
			for (int j = 0; j < length.length; j++)
			{			
				//Check if the entry exists and is an edge
				if (graph[j][i] != null && j != i)
				{
					parents.add(tasks.get(j));					     
				}	         
			}
			      
			tasks.add(new Task(i, graph[i][i], length[i], parents));
		}
		
		ArrayList<Processor> processors = new ArrayList<Processor>();
		//Create processors
		for(int i = 0; i < nbOfProcessors; i++)
		{
			processors.add(new Processor("P" + (i + 1)));
		}

		Collections.sort(tasks, new TaskComparator());
		
		ListScheduler.schedule(processors, tasks, graph, duplicateTasks);

		// print data
		{
			List<String> data = new ArrayList<String>();
			for(Task task : tasks)
			{
				data.add(task.toString() + "\n");
			}
			for (Processor processor : processors)
			{
				data.add("Schedule for processor " + processor.getName() 
						+ " :\n" + processor.getScheduleAsString());
			}
			dataToFile(new File(output + ".a3.out"), data, "");
		}

		
		// visualise
		{
			int maxTime = 0;
			for(Processor processor : processors)
	        {
	        	Set<Task> pTasks = processor.getTaskSchedule().keySet();
	        	
	        	for(Task task : pTasks)
	        	{
	        		Integer startTime = task.getStartTime(processor);
	        		
	        		if(startTime != null)
	        		{
	        			if(maxTime < startTime + task.getExecutionTime())
		        		{
		        			maxTime = startTime + task.getExecutionTime();
		        		}	
	        		}
	        	}
	        }	
			visualize("Task schedule", output + ".png", processors, maxTime + 1, 1, 800, 300);
		}
		
	}
	

	private static void dataToFile(File outputFile, List<String> data, String title)
	{
		System.out.println(title);
		
		FileWriter fstream = null;
		BufferedWriter out = null;
		try
		{
			fstream = new FileWriter(outputFile);
			out = new BufferedWriter(fstream);

			for(String outStr : data)
			{
				out.write(outStr);
				System.out.println(outStr);
			}
		}
		catch (Exception e)
		{
			System.err.println("Error: " + e.getMessage());
		}
		finally
		{
			if(out != null)
			{
				try
	            {
	                out.close();
	            }
	            catch(IOException e)
	            {
	                e.printStackTrace();
	            }
			}
            
			if(fstream != null)
			{
	            try
                {
	                fstream.close();
                }
                catch(IOException e)
                {
	                e.printStackTrace();
                }	
			}
		}
	}
	
	public static void visualize(String title, String filename, 
			ArrayList<Processor> processors, int timeSegment, int scale, int width, int height)
	{
		// create JobViewer
		JobViewer jobViewer = new JobViewer(width, height);
		
		// create BufferedImage and Graphics controller
		BufferedImage image = new BufferedImage(jobViewer.getWidth(), 
				jobViewer.getHeight(), BufferedImage.TYPE_INT_ARGB);
		Graphics2D graphics = image.createGraphics();
    	JobViewer.setGraphicsQuality(graphics);
    	graphics.setColor(Color.WHITE);
    	graphics.fillRect(0, 0, jobViewer.getWidth(), jobViewer.getHeight());
		
    	// create main panel
		Container mainPanel = new Container(10, 40, 
				jobViewer.getWidth() - 10 - 10, 
				jobViewer.getHeight() - 40 - 20);

        // create tasks and jobs
		Color taskColor = new Color(0, 200, 200);
		Color linkColor = new Color(200, 0, 200);
		
        for(Processor processor : processors)
        {
        	ProcessorCanvas pCanvas = new ProcessorCanvas(processor.getName(), graphics, timeSegment, scale);
        	
        	Set<Task> tasks = processor.getTaskSchedule().keySet();
        	
        	for(Task task : tasks)
        	{
        		Integer startTime = task.getStartTime(processor);
        		if(startTime != null)
        		{
        			JobCanvas jobCanvas = new JobCanvas(startTime, 
            				task.getExecutionTime(), pCanvas, task.getName(), taskColor);
            		pCanvas.getTasks().add(jobCanvas);	
        		}
        	}
        	
        	List<Edge> edges = processor.getEdgeSchedule();
        	for(Edge edge : edges)
        	{
        		LinkCanvas jobCanvas = new LinkCanvas(edge.getStartTime(), 
        				edge.getExecutionTime(), pCanvas, edge.getpSrc().getName() + 
        				"->" + edge.getpDst().getName(), linkColor);
        		pCanvas.getTasks().add(jobCanvas);
        	}
        	
        	mainPanel.getElements().add(pCanvas);
        }
        
        // layout everything
		mainPanel.layout(5);
		
		// make image
		jobViewer.generateImage(title, filename, mainPanel, image, graphics);
	}

}
